﻿Imports System.Reflection

''' <summary>
''' Represents the learned schema of a table based on attributes that had been applied to entity-based classes when the
''' schema was determined.
''' </summary>
Public Class TableSchema

    Private _database As DatabaseSchema = Nothing

    ''' <summary>
    ''' Gets the associated database object.
    ''' </summary>
    ''' <value>The database object that contains this table.</value>
    Public ReadOnly Property Database As DatabaseSchema
        Get
            Return _database
        End Get
    End Property

    Private _attribute As TableAttribute

    Private _entityType As Type

    Public ReadOnly Property EntityType As Type
        Get
            Return _entityType
        End Get
    End Property

    ''' <summary>
    ''' Gets the name of the table.
    ''' </summary>
    ''' <value>The name of the table.</value>
    Public ReadOnly Property Name As String
        Get
            Return _entityType.Name
        End Get
    End Property

    ''' <summary>
    ''' Gets the singular caption of the table, which should be displayed when referring to the entity as a single item.
    ''' </summary>
    ''' <value>The singular caption of the table.</value>
    Public ReadOnly Property SingularCaption As String
        Get
            If _attribute.SingularCaption <> "" Then Return _attribute.SingularCaption
            Return Name
        End Get
    End Property

    ''' <summary>
    ''' Gets the multiple caption of the table, which should be displayed when referring to the entity as multiple items.
    ''' </summary>
    ''' <value>The multiple caption of the table.</value>
    Public ReadOnly Property MultipleCaption As String
        Get
            If _attribute.MultipleCaption <> "" Then Return _attribute.MultipleCaption
            Return Name
        End Get
    End Property

    Private _fields As New List(Of FieldSchema)

    ''' <summary>
    ''' Allows retrieval of a field based on its name.
    ''' </summary>
    ''' <value>The schema of the requested field.</value>
    Default Public ReadOnly Property Fields(ByVal fieldName As String) As FieldSchema
        Get
            For Each Field As FieldSchema In _fields
                If Field.Name = fieldName Then Return Field
            Next
            Throw New Exception("The field '" + fieldName + "' does not exist in the table's schema.")
        End Get
    End Property

    ''' <summary>
    ''' Gets all of the fields that make up the table.
    ''' </summary>
    ''' <value>The schema of all of the fields in the table.</value>
    Public ReadOnly Property AllFields As FieldSchema()
        Get
            Return _fields.ToArray
        End Get
    End Property

    Private _primaryKey As String

    ''' <summary>
    ''' Gets the field name of the primary key in this table.
    ''' </summary>
    ''' <value>The field name of the primary key in the table.</value>
    Public ReadOnly Property PrimaryKey As String
        Get
            Return _primaryKey
        End Get
    End Property

    Private _childTables As New List(Of ChildTableSchema)

    ''' <summary>
    ''' Gets all of the associated child tables.
    ''' </summary>
    ''' <value>The child tables this table refers to.</value>
    Public ReadOnly Property ChildTables As ChildTableSchema()
        Get
            Return _childTables.ToArray
        End Get
    End Property

    Private _menuItems As New List(Of MenuItemAttribute)

    ''' <summary>
    ''' Gets the menu items associated with the table.
    ''' </summary>
    ''' <value>The menu items associated with the table.</value>
    Public ReadOnly Property MenuItems As MenuItemAttribute()
        Get
            Return _menuItems.ToArray
        End Get
    End Property

    Private Sub New(ByVal parent As DatabaseSchema, ByVal tableType As Type, ByVal attribute As TableAttribute)

        Me._database = parent
        Me._entityType = tableType
        Me._attribute = attribute

    End Sub

    Friend Shared Function Create(ByVal parent As DatabaseSchema, ByVal tableType As Type) As TableSchema

        ' Extract information about this table
        Dim Attribute As TableAttribute = GetAttribute(Of TableAttribute)(tableType)
        Dim Result As New TableSchema(parent, tableType, Attribute)

        ' Find columns in the table, based on properties in the given type
        Dim FoundPrimaryKey As Boolean = False
        For Each PropertyInfo As PropertyInfo In tableType.GetProperties

            ' Add this column to the table
            Dim FieldLabel As FieldAttribute = GetAttribute(Of FieldAttribute)(PropertyInfo)
            If FieldLabel IsNot Nothing Then Result._fields.Add(FieldSchema.Create(Result, PropertyInfo))

            ' Is this a primary key?
            Dim PrimaryKeyLabel As PrimaryKeyAttribute = GetAttribute(Of PrimaryKeyAttribute)(PropertyInfo)
            If PrimaryKeyLabel IsNot Nothing Then
                If FoundPrimaryKey Then Throw New Exception("The table '" + tableType.Name + "' already contains a primary key.")
                Result._primaryKey = PropertyInfo.Name
                FoundPrimaryKey = True
            End If

        Next

        ' Get child tables
        Dim ChildTableAttributes() As ChildTableAttribute = tableType.GetCustomAttributes(GetType(ChildTableAttribute), False)
        For Each CTA As ChildTableAttribute In ChildTableAttributes
            Result._childTables.Add(New ChildTableSchema(Result, CTA))
        Next

        ' Find events, based on properties in the given type
        Dim MenuItemAttributes() As MenuItemAttribute = tableType.GetCustomAttributes(GetType(MenuItemAttribute), False)
        For Each MIA As MenuItemAttribute In MenuItemAttributes
            Result._menuItems.Add(MIA)
        Next

        ' Ensure there was a primary key defined
        If Not FoundPrimaryKey Then Throw New Exception("A primary key must be defined for the '" + tableType.Name + "' table.")

        Return Result

    End Function

    Public Function CreateEntityType() As BaseEntity

        Dim Result As BaseEntity = Activator.CreateInstance(_entityType)
        Result.Schema = Me
        Return Result

    End Function

    Public Function GetPrimaryKeyValue(ByVal record As BaseEntity) As Object
        Return record.GetType.GetProperty(PrimaryKey).GetValue(record, Nothing)
    End Function

    Public Function GetStoreColumns() As FieldSchema()

        Dim Result As New List(Of FieldSchema)
        For Each Field As FieldSchema In _fields
            ' TODO: Augment such that things like computed fields are not included
            Result.Add(Field)
        Next
        Return Result.ToArray

    End Function

End Class
